package xin.altitude.commons.collection4;

import org.apache.commons.collections4.Equator;
import org.apache.commons.collections4.Factory;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.collections4.Transformer;

import java.util.Collection;
import java.util.List;

/**
 * <p>为｛@link List｝实例提供实用工具方法和装饰器</p>
 *
 * @author <a href="http://www.altitude.xin" target="_blank">Java知识图谱</a>
 * @author <a href="https://gitee.com/decsa/ucode-cms-vue" target="_blank">UCode CMS</a>
 * @author <a href="https://space.bilibili.com/1936685014" target="_blank">B站视频</a>
 **/
public class ListUtils {
    /**
     * 私有构造方法，保证工具类不可通过new实例化
     */
    private ListUtils() {
    }
    
    /**
     * <p>如果参数为<code>null</code>，则返回一个不可变的空集合</p>
     * <p>不可变是因为整个Java虚拟机可复用这一个空集合 {@link java.util.Collections#EMPTY_LIST}</p>
     * <p>空集合不可增加元素</p>
     *
     * @param <T>  集合元素类型
     * @param list 集合实例，参数可能为 <code>null</code>
     * @return 如果参数为<code>null</code>，则返回不可变的空集合实例
     */
    public static <T> List<T> emptyIfNull(final List<T> list) {
        return org.apache.commons.collections4.ListUtils.emptyIfNull(list);
    }
    
    /**
     * 如果集合实例{@code list}不为{@code null}，则直接返回 否则返回{@code defaultList}
     *
     * @param <T>         集合元素类型
     * @param list        集合实例，参数可能为 <code>null</code>
     * @param defaultList 如果集合实例为{@code null}，则用以替代的集合实例
     * @return 返回非{@code null}集合实例
     */
    public static <T> List<T> defaultIfNull(final List<T> list, final List<T> defaultList) {
        return org.apache.commons.collections4.ListUtils.defaultIfNull(list, defaultList);
    }
    
    /**
     * <p>将两个非空集合（参数不为{@code null}）取元素{@code 交集}后 生成新集合</p>
     * <p>两个元素是否相等由{@code <E>}类中的{@code equals}方法决定</p>
     * <p>如果数据实体类使用Lombok{@code @Data}注解，则{@code equals}方法被重写，实体类对应字段值相等，即认为两对象相等</p>
     *
     * @param <E>   集合元素类型
     * @param list1 第一个集合实例
     * @param list2 第二个集合实例
     * @return 返回两个集合实例中共性元素组成的新集合实例
     * @throws NullPointerException 如果任意一个参数为{@code null}
     */
    public static <E> List<E> intersection(final List<? extends E> list1, final List<? extends E> list2) {
        return org.apache.commons.collections4.ListUtils.intersection(list1, list2);
    }
    
    /**
     * <p>将两个非空集合（参数不为{@code null}）取元素{@code 差集}后 生成新集合</p>
     * <p>返回在集合实例{@code list1}中，却不在集合实例{@code list2}中的元素组成的新集合</p>
     * <p>两个元素是否相等由{@code <E>}类中的{@code equals}方法决定</p>
     * <p>如果数据实体类使用Lombok{@code @Data}注解，则{@code equals}方法被重写，实体类对应字段值相等，即认为两对象相等</p>
     *
     * @param <E>   集合元素类型
     * @param list1 被减集合实例
     * @param list2 集合实例
     * @return {@code 差集}之后的新集合实例
     * @throws NullPointerException 如果任意一个参数为{@code null}
     */
    public static <E> List<E> subtract(final List<E> list1, final List<? extends E> list2) {
        return org.apache.commons.collections4.ListUtils.subtract(list1, list2);
    }
    
    /**
     * <p>将两个非空集合（参数不为{@code null}）取元素{@code 并集}后 去重 生成新集合</p>
     * <p>两个元素是否相等由{@code <E>}类中的{@code equals}方法决定</p>
     * <p>如果数据实体类使用Lombok{@code @Data}注解，则{@code equals}方法被重写，实体类对应字段值相等，即认为两对象相等</p>
     *
     * @param <E>   集合元素类型
     * @param list1 第一个集合实例
     * @param list2 第二个集合实例
     * @return {@code 并集}去重后形成的新集合实例
     * @throws NullPointerException 如果任意一个参数为{@code null}
     */
    public static <E> List<E> sum(final List<? extends E> list1, final List<? extends E> list2) {
        return org.apache.commons.collections4.ListUtils.sum(list1, list2);
    }
    
    /**
     * <p>将两个非空集合（参数不为{@code null}）取元素{@code 并集}后 不去重 生成新集合</p>
     * <p>不去重意味着新生成的集合实例{@code size}等于原集合之和</p>
     * <p>两个元素是否相等由{@code <E>}类中的{@code equals}方法决定</p>
     * <p>如果数据实体类使用Lombok{@code @Data}注解，则{@code equals}方法被重写，实体类对应字段值相等，即认为两对象相等</p>
     *
     * @param <E>   集合元素类型
     * @param list1 第一个集合实例
     * @param list2 第二个集合实例
     * @return {@code 并集}不去重后形成的新集合实例
     * @throws NullPointerException 如果任意一个参数为{@code null}
     */
    public static <E> List<E> union(final List<? extends E> list1, final List<? extends E> list2) {
        return org.apache.commons.collections4.ListUtils.union(list1, list2);
    }
    
    /**
     * <p>通过断言器过滤集合中数据 如果参数<code>predicate</code>为{@code null}，则返回无任何元素的空集合</p>
     * <p>提示：本方法在Java 8之后有更好的替代实现，通过流完成数据过滤更方便：</p>
     * <pre>
     *     inputCollection.stream().filter(predicate)
     * </pre>
     *
     * @param inputCollection 集合实例 参数为<code>null</code>会引发空指针异常
     * @param predicate       断言器 允许为<code>null</code>
     * @param <E>             集合中元素的类型
     * @return 过滤后得到的元素形成的新集合实例
     */
    public static <E> List<E> select(final Collection<? extends E> inputCollection, final Predicate<? super E> predicate) {
        return org.apache.commons.collections4.ListUtils.select(inputCollection, predicate);
    }
    
    
    /**
     * <p>通过断言器反向过滤集合中数据 如果参数<code>predicate</code>为{@code null}，则返回无任何元素的空集合</p>
     * <p>提示：本方法在Java 8之后有更好的替代实现，通过流完成数据过滤更方便：</p>
     * <pre>
     *     inputCollection.stream().filter(!predicate)
     * </pre>
     *
     * @param inputCollection 集合实例 参数为<code>null</code>会引发空指针异常
     * @param predicate       断言器 允许为<code>null</code>
     * @param <E>             集合中元素的类型
     * @return 过滤后得到的元素形成的新集合实例
     */
    public static <E> List<E> selectRejected(final Collection<? extends E> inputCollection, final Predicate<? super E> predicate) {
        return org.apache.commons.collections4.ListUtils.selectRejected(inputCollection, predicate);
    }
    
    /**
     * <p>检验两个集合实例是否由同一批元素组成的集合</p>
     * <p>如果两个集合{@code size}相等并且互相差集之后，均为空集，则两集合实例相等</p>
     * <p>集合相等并不要求对应下标元素<i>equal</i>相等，可乱序，只要组成集合元素整体相等即可</p>
     *
     * @param list1 第一个集合实例 允许为{@code null}
     * @param list2 第二个集合实例 允许为{@code null}
     * @return 比较后两集合是否相等
     */
    public static boolean isEqualList(final Collection<?> list1, final Collection<?> list2) {
        return org.apache.commons.collections4.ListUtils.isEqualList(list1, list2);
    }
    
    public static int hashCodeForList(final Collection<?> list) {
        return org.apache.commons.collections4.ListUtils.hashCodeForList(list);
    }
    
    /**
     * <p>本方法是{@link org.apache.commons.collections4.ListUtils#intersection(List, List)}交集的另一种实现</p>
     * <p>如果集合{@code collection}是集合{@code retain}的真子集，则返回以集合{@code collection}元素<i>去重</i>后组成的新集合实例</p>
     * <p>如果集合{@code retain}是集合{@code collection}的真子集，则返回以集合{@code retain}元素<i>去重</i>后组成的新集合实例</p>
     * <p>集合{@code retain}与集合{@code collection}<i>交集</i><i>去重</i>后元素组成的新集合实例</p>
     * <pre>
     *     collection           retain              result
     *     [1,1,3]              [1,3]               [1,3]
     *     [1,3,3]              [1,3,4]             [1,3]
     *     [1,2,3]              [2,3,4]             [2,3]
     * </pre>
     *
     * @param <E>        集合元素类型
     * @param collection 集合实例
     * @param retain     集合实例
     * @return 保留元素组成的新集合实例 返回的集合实例不是{@code collection}，而是新创建的集合
     * @throws NullPointerException 如果任意一个参数为{@code null}
     */
    public static <E> List<E> retainAll(final Collection<E> collection, final Collection<?> retain) {
        return org.apache.commons.collections4.ListUtils.retainAll(collection, retain);
    }
    
    /**
     * <p>本方法是{@link org.apache.commons.collections4.ListUtils#subtract(List, List)}<i>差集</i>的另一种实现</p>
     * <pre>
     *     collection           remove              result
     *     [1,1,3,5]            [1,3]               [5]
     *     [1,3,3,5]            [1,3,4]             [5]
     *     [1,2,3]              [2,3,4]             [1]
     * </pre>
     *
     * @param <E>        集合元素类型
     * @param collection 集合实例
     * @param remove     集合实例
     * @return 移除元素组成的新集合实例 返回的集合实例不是{@code collection}，而是新创建的集合
     * @throws NullPointerException 如果任意一个参数为{@code null}
     */
    public static <E> List<E> removeAll(final Collection<E> collection, final Collection<?> remove) {
        return org.apache.commons.collections4.ListUtils.removeAll(collection, remove);
    }
    
    /**
     * 将集合实例转换成线程安全的集合实例
     *
     * @param list 集合实例
     * @param <E>  集合元素类型
     * @return 线程安全的集合实例
     */
    public static <E> List<E> synchronizedList(final List<E> list) {
        return org.apache.commons.collections4.ListUtils.synchronizedList(list);
    }
    
    public static <E> List<E> unmodifiableList(final List<? extends E> list) {
        return org.apache.commons.collections4.ListUtils.unmodifiableList(list);
    }
    
    /**
     * <p>按照{@code predicate}断言条件过滤集合</p>
     * <p>使用流同样能够达到过滤集合元素的需求</p>
     * <pre>
     *     list.stream().filter(predicate)
     * </pre>
     *
     * @param list      集合实例
     * @param predicate {@link org.apache.commons.collections4.Predicate}布尔断言器
     * @param <E>       集合元素类型
     * @return 过滤后的集合
     */
    public static <E> List<E> predicatedList(final List<E> list, final Predicate<E> predicate) {
        return org.apache.commons.collections4.ListUtils.predicatedList(list, predicate);
    }
    
    public static <E> List<E> transformedList(final List<E> list, final Transformer<? super E, ? extends E> transformer) {
        return org.apache.commons.collections4.ListUtils.transformedList(list, transformer);
    }
    
    /**
     * <p>创建一个懒惰的集合实例 当集合中的元素被访问时 获取动态的值</p>
     * <p>在输入集合的基础上 额外增加一个延迟实例化的动态元素 当且仅当在访问时 才能获取到值</p>
     * <pre>
     *     List&lt;Integer&gt; lazyList = ListUtils.lazyList(new ArrayList&lt;&gt;(), () -&gt; LocalDateTime.now().getNano());
     *     Integer nano = lazyList.get(0);
     * </pre>
     * <p>示例中输入的是空集合，那么第一个元素便是<i>动态元素</i> 每次访问时可获取到不同的值</p>
     *
     * @param list    普通的集合实例
     * @param factory 延迟实例化集合中元素的工厂 函数式接口 相当于{@link java.util.function.Supplier}的功能
     * @param <E>     集合中元素的类型
     * @return {@link org.apache.commons.collections4.list.LazyList}实例
     * @throws NullPointerException 如果参数<code>list</code>或者<code>factory</code>为<code>null</code>
     */
    public static <E> List<E> lazyList(final List<E> list, final Factory<? extends E> factory) {
        return org.apache.commons.collections4.ListUtils.lazyList(list, factory);
    }
    
    public static <E> List<E> lazyList(final List<E> list, final Transformer<Integer, ? extends E> transformer) {
        return org.apache.commons.collections4.ListUtils.lazyList(list, transformer);
    }
    
    public static <E> List<E> fixedSizeList(final List<E> list) {
        return org.apache.commons.collections4.ListUtils.fixedSizeList(list);
    }
    
    public static <E> int indexOf(final List<E> list, final Predicate<E> predicate) {
        return org.apache.commons.collections4.ListUtils.indexOf(list, predicate);
    }
    
    
    /**
     * <p>返回两个字符串集合中最长公共字符数组</p>
     * <pre>
     *      List&lt;String&gt; a = Arrays.asList("a", "b", "e", "d", "c");
     *      List&lt;String&gt; b = Arrays.asList("a", "c", "d");
     *      // 输出结果 "a","d"
     *      List&lt;String&gt; stringList = ListUtils.longestCommonSubsequence(a, b);
     * </pre>
     * <p>公共字符不要求相邻连续 最长的含义是按照字符出现位置最大匹配</p>
     * <p>比如示例中字符c和d矛盾按照最长的策略选出呢？c（2*5=10）d（3*4=12）通过下标小技巧，便能选出最终的d</p>
     *
     * @param a   字符串集合
     * @param b   字符串集合
     * @param <E> {@code String}类型
     * @return 字符串集合
     */
    public static <E> List<E> longestCommonSubsequence(final List<E> a, final List<E> b) {
        return org.apache.commons.collections4.ListUtils.longestCommonSubsequence(a, b);
    }
    
    public static String longestCommonSubsequence(final CharSequence a, final CharSequence b) {
        return org.apache.commons.collections4.ListUtils.longestCommonSubsequence(a, b);
    }
    
    public static <E> List<E> longestCommonSubsequence(final List<E> a, final List<E> b, final Equator<? super E> equator) {
        return org.apache.commons.collections4.ListUtils.longestCommonSubsequence(a, b, equator);
    }
    
    
    /**
     * <p>将集合实例{@code list}按照{@code size}分块，形成包含子集合的新集合</p>
     * <p>例如原始集合[a，b，c，d，e]按照每个子集合最大长度为{@code 3}进行分块，则得到[[a，b，c]，[d，e]]</p>
     * <p>返回列表的连续子列表，每个子列表大小相同（最终列表可能更小）</p>
     *
     * @param <T>  集合元素类型
     * @param list 集合实例
     * @param size 子集合最大长度
     * @return 连续有序的子集合实例
     * @throws NullPointerException     如果集合实例为{@code null}
     * @throws IllegalArgumentException 如果参数{@code size}不是正数
     */
    public static <T> List<List<T>> partition(final List<T> list, final int size) {
        return org.apache.commons.collections4.ListUtils.partition(list, size);
    }
}
